package com.zhi.common.validate.executor;

import com.zhi.common.validate.common.BizValidate;
import com.zhi.common.validate.common.BizValidatorContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 动态校验执行器
 * @author ftz-lover
 * @version 1.0
 * @date 2023/9/10 13:30
 */
@Slf4j
public class BizValidatorExecutor {
    /**
     * 单例
     */
    private static final BizValidatorExecutor INSTANCE = new BizValidatorExecutor();

    /**
     * 是否完成初始化
     */
    private static Boolean INITIALIZED = false;

    /**
     * 类扫描器
     */
    private ClassPathBeanDefinitionScanner scanner;

    /**
     * spring上下文
     */

    private ApplicationContext applicationContext;

    /**
     * 是否需要动态校验缓存
     */
    private final Map<Method, Boolean> needValidateCacheMap = new ConcurrentHashMap<>();

    /**
     * 启动动态校验框架
     * @param applicationContext spring上下文
     * @param inspectPath 自检扫描的实现类路径
     * @throws Exception
     */
    public static void init(ApplicationContext applicationContext, BeanDefinitionRegistry registry, String inspectPath) throws Exception {
        if (INITIALIZED) {
            return;
        }
        // 初始化参数
        INSTANCE.scanner = new ClassPathBeanDefinitionScanner(registry);
        INSTANCE.applicationContext = applicationContext;
        INITIALIZED = true;
    }

    public static void validate(Class<?> clazz, Method method, Object[] params) throws Throwable {
        BizValidate classAnnotation = AnnotationUtils.findAnnotation(clazz, BizValidate.class);
        BizValidate methodAnnotation = AnnotationUtils.findAnnotation(method, BizValidate.class);

        // 没有动态校验器或判断注解不需要校验，直接返回
        if (!needValidate(clazz, classAnnotation, method, methodAnnotation)) {
            return;
        }

        // 组装上下文
        BizValidatorContext context = BizValidatorContext.create(clazz, classAnnotation, method, methodAnnotation, params);
        // 执行校验
        try {
            Object bizValidator = INSTANCE.applicationContext.getBean(context.getBizValidatorName());
            Method validateMethod = bizValidator.getClass()
                .getMethod(context.getValidateMethodName(), context.getApiImplMethod().getParameterTypes());
            validateMethod.invoke(bizValidator, context.getParams());
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }


    /**
     * 判断是否需要校验
     * @param clazz
     * @param classAnnotation
     * @param method
     * @param methodAnnotation
     * @return
     */
    private static Boolean needValidate(Class<?> clazz, BizValidate classAnnotation,
                                        Method method, BizValidate methodAnnotation) {
        // 从缓存取是否需要校验
        if (INSTANCE.needValidateCacheMap.containsKey(method)) {
            return true;
        }

        // 非公共类或非公共方法不进行校验
        if (!Modifier.isPublic(clazz.getModifiers()) || !Modifier.isPublic(method.getModifiers())) {
            return false;
        }

        // 类和方法都没有注解不进行校验
        if (Objects.isNull(classAnnotation) && Objects.isNull(methodAnnotation)) {
            return false;
        }

        // 类注解上如果ignore=true不进行校验
        if (Objects.nonNull(classAnnotation) && classAnnotation.ignore()) {
            return false;
        }

        // 方法注解上如果ignore=true不进行校验
        if (Objects.nonNull(methodAnnotation) && methodAnnotation.ignore()) {
            return false;
        }

        INSTANCE.needValidateCacheMap.put(method, true);
        return true;
    }

}

